<chapter xml:id="logger.h">
<title><tt>__vic/logger.h</tt></title>

<chapter xml:id="logger">
<title><tt>logger</tt></title>

<code-block lang="C++"><![CDATA[
class logger : private non_copyable
{
public:
    enum class severity : unsigned char
    {
        trace,
        debug,
        info,
        notice,
        warning,
        error,
        fatal
    };
    using severity_t = severity; // use this alias as a type name
    struct output
    {
        virtual void publish_record(severity_t , const char * , size_t ) = 0;
    protected:
        ~output() = default;
    };
    class settings_t
    {
        struct output &output() const;
        severity_t level() const;
    };
    class record;

    explicit logger(output &out, severity_t = severity::info);
    explicit logger(settings_t s);
    ~logger();

    severity_t level() const;
    void level(severity_t new_level);
    settings_t settings() const;
    output &reset_output(output &out);
    output &get_output();
    const output &get_output() const;

    static constexpr size_t min_buffer_size = ...;
    void shrink_buffer(size_t limit);

    void message(severity_t severity, const char *msg, size_t msg_len);
#if __cpp_lib_string_view // C++17
    void message(severity_t severity, std::string_view msg);

    void trace(std::string_view msg);
    void debug(std::string_view msg);
    void info(std::string_view msg);
    void notice(std::string_view msg);
    void warning(std::string_view msg);
    void error(std::string_view msg);
    void fatal(std::string_view msg);
#else // until C++17
    void message(severity_t severity, const char *msg);
    void message(severity_t severity, const std::string &msg);

    void trace(const char *msg);
    void debug(const char *msg);
    void info(const char *msg);
    void notice(const char *msg);
    void warning(const char *msg);
    void error(const char *msg);
    void fatal(const char *msg);

    void trace(const std::string &msg);
    void debug(const std::string &msg);
    void info(const std::string &msg);
    void notice(const std::string &msg);
    void warning(const std::string &msg);
    void error(const std::string &msg);
    void fatal(const std::string &msg);
#endif

#if __cpp_lib_format >= 202106L // C++20 + P2508
    template<class... Args>
    void format(severity_t s,
        std::format_string<Args...> fmt, Args&&... args);

    template<class Arg1, class... Args>
    void trace(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void debug(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void info(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void notice(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void warning(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void error(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
    template<class Arg1, class... Args>
    void fatal(std::format_string<Arg1,Args...> fmt,
                                        Arg1 &&arg1, Args&&... args);
#endif

    record trace();
    record debug();
    record info();
    record notice();
    record warning();
    record error();
    record fatal();

    bool trace_visible() const;
    bool debug_visible() const;
    bool info_visible() const;
    bool notice_visible() const;
    bool warning_visible() const;
    bool error_visible() const;
    bool fatal_visible() const;
};
class logger::record
{
public:
    record(logger &log, severity_t sev);
    ~record();

    record append(const char *str, size_t str_len);

    template<class T> record operator<<(const T &v);
};
template<class T> struct log_value
{
    static void to_text(const T &v, std::string &s);
};
const char *to_string(logger::severity_t s);
#if __cpp_lib_string_view // C++17
constexpr std::string_view to_string_view(logger::severity s);
#endif
]]></code-block>

<p>Logging front-end. Provides building of the
log records using operator <tt>&lt;&lt;</tt>, like <tt>iostream</tt>. Each log
record has the associated severity. The logger itself can filter out the
records by severity. There are 7 predefined levels of the severity (in
ascending order):</p>
<list style="numbered">
    <item>TRACE - detailed debug,</item>
    <item>DEBUG - debug,</item>
    <item>INFO - informational,</item>
    <item>NOTICE - normal but significant event,</item>
    <item>WARNING - insignificant error or suspicious situation,</item>
    <item>ERROR - severe error but the application can continue,</item>
    <item>FATAL - critical unrecoverable error, the application can't
        continue.</item>
</list>

<p>INFO is the default logging level but any other can be chosen. If severity
of the log message (record) is below the logging level, it will be ignored and
will not be published in the log's output.</p>

<p>For creation of messages with the required severity, the set of functions
with the same name as the severity is available. For instance <tt>info()</tt>
for INFO messages. Or alternatively the universal function <tt>message()</tt>
can be used, in which the severity is the argument. Usually the specific
functions should be used.</p>

<p>There are two ways of logging. The first is plain and common:</p>
<code-block lang="C++">
log.trace("Trace message");
log.debug("Debug message");
log.info("Info message");
log.notice("Notice");
log.warning("Warning");
log.error("Recoverable error");
log.fatal("Fatal error");
</code-block>

<p>The second is slightly more complex but provides more capabilities:</p>
<code-block lang="C++"><![CDATA[
log.error() << "Cannot open file " << filename << '!';
log.warning() << "Loop iteration no " << i;
]]></code-block>

<p>The call without parameters creates the object of type
<tt>logger::record</tt> with the corresponding severity. Futher, the message
is formed using operators <tt>&lt;&lt;</tt>. The message will be output to the
log at the end of the full-expression (term from the Standard).</p>

<p>If the message can't or shouldn't be formed with a single expression,
the named object of type <tt>logger::record</tt> has to be created, and parts
of the message have to be written to it. The resulting message will be output
to the log by it's destructor:</p>
<code-block lang="C++"><![CDATA[
{
    logger::record rec = log.info(); // Begin new record
    rec << "List elements: ";
    for(auto el : list) rec << el << ", ";
    // Constructed record will be printed to the log when the block exits
}
]]></code-block>

<note>In order to optimize the performace, use the plain functional notation
when the complete message is available:</note>
<code-block lang="C++"><![CDATA[
log.info("Message");
// but not
log.info() << "Message";
]]></code-block>

<p>Output of the records with the severities DEBUG and TRACE is usually disabled.
Such records will not be published in the log but the program will waste
time to format it. Therefore before creating any debug message using operator
<tt>&lt;&lt;</tt> one should check if debug is enabled using
<tt>debug_visible()</tt> or <tt>trace_visible()</tt> call:</p>
<code-block lang="C++"><![CDATA[
if(log.debug_visible())
    log.debug() << ...; // build the message
]]></code-block>
<p>This advice doesn't cover plain calls <tt>debug(msg)</tt> and
<tt>trace(msg)</tt>, which have a prepared message already and don't perform any
formatting.</p>

<p>To use <tt>logger</tt> one has to implement abstract base class
<tt>logger::output</tt> (override <tt>publish_record()</tt>). The implementation
has to output the passed record somewhere, e.g. to file, terminal or DB. The
output specified during <tt>logger</tt> construction can be replaced later
using <tt>reset_output()</tt>.</p>

<section><title>Class members</title>

<synopsis>
<prototype>severity::trace</prototype>
<prototype>severity::debug</prototype>
<prototype>severity::info</prototype>
<prototype>severity::notice</prototype>
<prototype>severity::warning</prototype>
<prototype>severity::error</prototype>
<prototype>severity::fatal</prototype>
<p>Severity constants. Use this form for both C++11 and C++98 mode.</p>
</synopsis>

<synopsis>
<prototype>typename severity_t</prototype>
<p>Use this identifier as a type name if your code has to be C++98-compatible.
Since C++11 it is just a synonym for <tt>severity</tt>.</p>
</synopsis>

<synopsis>
<prototype>class output</prototype>
<p>Logging back-end interface.</p>
</synopsis>

<synopsis>
<prototype>void output::publish_record(severity_t sev, const char *buf, size_t buf_len)</prototype>
<p>The implementaion of this pure virtual function has to output the content of
<tt>buf</tt> to the log as one record. <tt>buf_len</tt> is the length of
<tt>buf</tt>. The function is always called with <tt>sev >= level()</tt>.
The implementation can rely on it.</p>
</synopsis>

<synopsis>
<prototype>class settings_t</prototype>
<p>Keeps the logger settings: logging level and reference to output
(<tt>level()</tt> + <tt>get_output()</tt>).</p>
</synopsis>

<synopsis>
<prototype>explicit logger(output &amp;out, severity_t level = severity::info)</prototype>
<p>Creates logger with the specified output and logging level. The output object
must outlive the logger object!</p>
<postcondition><tt>this->level() == level &amp;&amp; &amp;this->get_output() == &amp;out</tt></postcondition>
</synopsis>

<synopsis>
<prototype>explicit logger(settings_t s)</prototype>
<p>Creates logger using the specified settings.</p>
</synopsis>

<synopsis>
<prototype>severity_t level() const</prototype>
<p>Returns current logging level.</p>
</synopsis>

<synopsis>
<prototype>void level(severity_t new_level)</prototype>
<p>Sets the logging level.</p>
<postcondition><tt>level() == new_level</tt></postcondition>
</synopsis>

<synopsis>
<prototype>settings_t settings() const</prototype>
<p>Returns current logging settings.</p>
</synopsis>

<synopsis>
<prototype>output &amp;reset_output(output &amp;out)</prototype>
<p>Sets new output and returns the previous one.</p>
<postcondition><tt>&amp;get_output() == &amp;out</tt></postcondition>
</synopsis>

<synopsis>
<prototype>output &amp;get_output()</prototype>
<prototype>const output &amp;get_output() const</prototype>
<p>Returns reference to the current logging output.</p>
</synopsis>

<synopsis>
<prototype>static constexpr size_t min_buffer_size</prototype>
<p>Minimal size of the internal buffer in bytes.</p>
</synopsis>

<synopsis>
<prototype>void shrink_buffer(size_t limit)</prototype>
<p>Sets the internal buffer size to <tt>min_buffer_size</tt> if it is more than
<tt>limit</tt> bytes. Allows to restrict the buffer growth when long records
are formed.</p>
</synopsis>

<synopsis>
<prototype>void message(severity_t severity, const char *msg, size_t msg_len)</prototype>
<prototype>void message(severity_t severity, std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void message(severity_t severity, const char *msg) <badge>until C++17</badge></prototype>
<prototype>void message(severity_t severity, const std::string &amp;msg) <badge>until C++17</badge></prototype>
<p>Writes the message with the specified severity.</p>
</synopsis>

<synopsis>
<prototype>void trace(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void trace(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void trace(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void debug(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void debug(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void debug(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void info(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void info(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void info(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void notice(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void notice(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void notice(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void warning(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void warning(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void warning(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void error(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void error(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void error(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<prototype>void fatal(std::string_view msg) <badge>C++17</badge></prototype>
<prototype>void fatal(const char *msg) <badge>until C++17</badge></prototype>
<prototype>void fatal(const std::string &amp;msg) <badge>until C++17</badge></prototype>
<p>Writes the message with the corresponding severity.</p>
</synopsis>

<synopsis>
<prototype><![CDATA[template<class... Args>
void format(severity_t s,
    std::format_string<Args...> fmt, Args&&... args)]]> <badge>C++20</badge></prototype>
<p>Formats the message using the specified format string and arguments (like
<tt>std::format</tt> does) then writes it with the specified severity.</p>
</synopsis>

<synopsis>
<prototype><![CDATA[template<class Arg1, class... Args>
void trace(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void debug(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void info(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void notice(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void warning(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void error(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<prototype><![CDATA[template<class Arg1, class... Args>
void fatal(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args)]]> <badge>C++20</badge></prototype>
<p>Formats the message using the specified format string and arguments (like
<tt>std::format</tt> does) then writes it with the corresponding severity.</p>
</synopsis>

<synopsis>
<prototype>logger::record trace()</prototype>
<prototype>logger::record debug()</prototype>
<prototype>logger::record info()</prototype>
<prototype>logger::record notice()</prototype>
<prototype>logger::record warning()</prototype>
<prototype>logger::record error()</prototype>
<prototype>logger::record fatal()</prototype>
<p>Creates new record with the corresponding severity. Message parts can be
added using operators <tt>&lt;&lt;</tt>.</p>
</synopsis>

<synopsis>
<prototype>bool trace_visible() const</prototype>
<prototype>bool debug_visible() const</prototype>
<prototype>bool info_visible() const</prototype>
<prototype>bool notice_visible() const</prototype>
<prototype>bool warning_visible() const</prototype>
<prototype>bool error_visible() const</prototype>
<prototype>bool fatal_visible() const</prototype>
<p>Returns <tt>true</tt> if a record with the corresponding severity will be
published. Usage of this functions enables to avoid formatting of the
messages which eventually will not be published.</p>
</synopsis>

<synopsis>
<prototype>record::record(logger &amp;log, severity_t sev)</prototype>
<p>Creates the log record with the specified severity. Usually the <tt>logger</tt>'s
functions like <tt>info()</tt> without parameters should be used instead.</p>
</synopsis>

<synopsis>
<prototype>record::~record()</prototype>
<p>Outputs the constructed record to the log.</p>
</synopsis>

<synopsis>
<prototype>record record::append(const char *str, size_t str_len)</prototype>
<p>Appends the string to the message.</p>
</synopsis>

<synopsis>
<prototype>template&lt;class T> record record::operator&lt;&lt;(const T &amp;v)</prototype>
<p>The set of inserters for various data types. The specified value is
converted to text using <tt>log_value&lt;T>::to_text()</tt> call.</p>
</synopsis>

</section>

<section><title>Value-to-text converter</title>

<synopsis>
<prototype>template&lt;class T> void log_value&lt;T>::to_text(const T &amp;v, std::string &amp;s)</prototype>
<p>Converts a value of type <tt>T</tt> to text using unqualified
<xref to="to_text_append"/> call.</p>
<note>This function (class with static function, to be precise) can be
specialized for your <tt>T</tt>. But usually you just define
<tt>to_text_append(const T &amp;, std::string &amp;)</tt>.</note>
</synopsis>

</section>

<section><title>Free functions</title>

<synopsis>
<prototype>const char *to_string(logger::severity_t s)</prototype>
<prototype>constexpr std::string_view to_string_view(logger::severity s) <badge>C++17</badge></prototype>
<p>Returns text label for the specified severity that can be printed to the
log. For example, <tt>"DEBUG"</tt> will be returned for
<tt>severity::debug</tt>.</p>
</synopsis>

</section>

<section><title>Example</title>

<code-block lang="C++"><![CDATA[
/////////////////////////////////////////////////////////////////////////////
// Output messages to std::clog with the severity label
class coutput : public __vic::logger::output
{
public:
    void publish_record(__vic::logger::severity_t s,
                            const char *rec, size_t rec_n)
    {
        std::clog << to_string(s) << ": ";
        std::clog.write(rec, rec_n) << std::endl;
    }
};
/////////////////////////////////////////////////////////////////////////////

int main()
{
    coutput log_output:
    __vic::logger log(log_output, __vic::logger::severity::debug);

    log.info("Application is started");

    for(int i = 0; i < 5; i++)
        log.debug() << "Loop i = " << i;

    log.warning("Application end");
}
]]></code-block>

<p>Output:</p>

<tty>
INFO: Application is started
DEBUG: Loop i = 0
DEBUG: Loop i = 1
DEBUG: Loop i = 2
DEBUG: Loop i = 3
DEBUG: Loop i = 4
WARNING: Application end
</tty>

</section>

</chapter>

</chapter>
